<?php
/**
 * DB基本処理クラス
 * 
 * 1.0 2010.11.20 初期リリース
 * 
 * @filesource FTDB_Common.class.php
 * @package forty-two
 * @subpackage forty-two
 * @version 1.0
 */


// 標準選択DB設定名定義
if(!defined('DEFAULT_DB_CONNECTION_NAME')) {
    define('DEFAULT_DB_CONNECTION_NAME', 'base');
}
// PHP標準定数「DIRECTORY_SEPARATOR」のエイリアス定数DS未定義時
if (!defined('DS')) {
    define('DS', DIRECTORY_SEPARATOR);
}
// DB各種情報書き出し先パス定義
if(!defined('PATH_DB_INFO')) {
    define('PATH_DB_INFO', '/tmp/dbCache');
}
// DB構造情報キャッシュファイル更新間隔定義(単位秒)
if(!defined('DATABASE_CASH_UPDATE_INTERVAL')) {
    define('DATABASE_CASH_UPDATE_INTERVAL', 60*60);
}
// DB接続設定定義
if(!defined('DATABASE_SETTINGS')) {
    // エラー発生
    trigger_error('DB接続用定数定義がありません。[DATABASE_SETTINGS]');
}
// パス定義
if(!defined('PATH_PROCESS_DATA')) {
    // エラー発生
    trigger_error('プロセス用PATHの定数定義がありません。[PATH_PROCESS_DATA]');
}
// パス定義
if(!defined('PATH_DATA_LIB')) {
    define('PATH_DATA_LIB', dirname(__FILE__));
}
// キャッシュファイルプレフィックス定義
if(!defined('PREFIX_DB_CACHE_FILE')) {
    define('PREFIX_DB_CACHE_FILE', 'dbCache');
}



// 処理文字コード
if(!defined('CHARA_SET_INNER')) {
    define('CHARA_SET_INNER', 'UTF-8');
}
// データベース文字コード
if(!defined('CHARA_SET_DATABASE')) {
    define('CHARA_SET_DATABASE', 'UTF-8');
}

/**
 * DB基本処理クラス
 * 
 * 1.0 2010.11.20 初期リリース
 * 
 * @author koto
 * @package forty-two
 * @version 1.0
 */
class FTDB_Common
{
    
    /**
     * オーバーライド前提変数定義
     */
    // string オブジェクト目的テーブル名保持用変数
    var $_tableName = '';
    
    /**
     * 処理参照用変数定義
     */
    // array データベース接続用情報配列
    var $_databaseSetting = array();
    // string 標準DB接続情報指定
    var $_defaultDbSettingName = '';
    // 利用可能なDB名
    var $_validDbTypes = array('mysql','pgsql');
    
    var $_errorMsg = array();
    var $_errorMsgDetail = array();
    
    /**
     * ファクトリーメソッド
     *
     * DBオブジェクトのインスタンスを作成します。
     * 第一引数にはサブクラスを指定します。
     * 
     * @since 1.0
     * @static
     * @access public
     * @param string  $className サブクラス名
     * @param string  $db 利用するDB接続情報指定
     * @return object
     */
    static function & factory($tableName = '', $db = DEFAULT_DB_CONNECTION_NAME)
    {
        $fileName = 'FTDB_' . $db . '_' . $tableName;
        $objName = 'FTDB_' . $tableName;
        
        // 既に定義済みの場合は既存オブジェクトを返す
        if (isset($GLOBALS['forty-two']['FTDB']['obj'][$db][$tableName])
             && is_object($GLOBALS['forty-two']['FTDB']['obj'][$db][$tableName])
        ) {
            return $GLOBALS['forty-two']['FTDB']['obj'][$db][$tableName];
        // 指定テーブル専用オブジェクトファイルが存在しなくてもOKなように分岐させる
        } else if (is_file(PATH_PROCESS_DATA . $fileName . '.class.php')) {
            // 対応定義ファイルを読み込み実体化
            require_once $fileName . '.class.php';
            $obj =& new $objName($tableName, $db);
            return $obj;
        } else {
            // 自分を実体化
            $obj =& new FTDB_Common($tableName, $db);
        }
        
        if ( is_object($obj) && $obj !== FALSE) {
            // オブジェクトの保存
            $GLOBALS['forty-two']['FTDB']['obj'][$db][$tableName] = $obj;
        } else {
            return $this->_setError('Fails to factory.$1['.$tableName.'] $2['.$db.']');
        }
        
        return $obj;
    }
    
    
    
    /**
     * コンストラクタ
     *
     * オブジェクト生成時に初期処理を行います。
     * 
     * @since 1.0
     * @access public
     * @param string  $className サブクラス名
     * @return object
     */
    function FTDB_Common($tableName = '', $db = DEFAULT_DB_CONNECTION_NAME)
    {
        // 指定テーブル名を保持
        if (strlen($tableName)>0) {
            $this->_tableName = $tableName;
        }
        
        // 指定DBを保持
        if (strlen($db)>0) {
            $this->_dbName = $db;
        }
        
        // DB接続情報の保持
        if (!$this->_databaseObjectInitialize()) {
            return FALSE;
        }
        
        // DB接続
        if ($this->_connect($db)) {
            if (!$this->_setnames(CHARA_SET_DATABASE, $db)) {
                return $this->_setError('set names FALSE.['.CHARA_SET_DATABASE.']['.$db.']');
            }
            return TRUE;
        } else {
            return $this->_setError('is not database connect.['.$db.']');
        }
    }
    
    
    
    /**
     * DB用オブジェクト初期処理
     * 
     * DB接続情報の保持　と　DB用オブジェクト生成処理を行います。
     * 
     * @since 1.0
     * @access protected
     * @return boolean
     */
    function _databaseObjectInitialize()
    {
        $databaseSetting = unserialize(DATABASE_SETTINGS);
        $type = $databaseSetting[$this->_dbName]['type'];
        
        // DB接続情報確認
        if (!$this->_checkDatabaseSettings($databaseSetting)) {
            return $this->_setError('is not database object.['.$type.']');
        } else {
            // 指定DBオブジェクトの実体化
            $this->_databaseSetting = $databaseSetting;
            $obj = $this->_databaseObjectInstantiation(strtolower($type));
            
            // エラーチェック
            if (is_object($obj)) {
                $this->_dbObj = $obj;
                return TRUE;
            } else {
                return $this->_setError('is not database object.['.$type.']');
            }
        }
        return FALSE;
    }
    
    
    
    /**
     * DB接続設定定義確認処理
     * 
     * DB接続設定の定義が書式敵に問題ないか確認します。
     * 
     * @access private
     * @return boolean
     */
    function _checkDatabaseSettings(&$dbSettings = array())
    {
        // 配列であり1つ以上定義されているか
        if (is_array($dbSettings) && count($dbSettings)>0) {
            // 標準選択DB設定名定義が定義されたか確認フラグ
            $defaultDbConnectionName = FALSE;
            
            // 設定全チェック
            foreach ($dbSettings as $k => $row) {
                if ($k == DEFAULT_DB_CONNECTION_NAME) {
                    $defaultDbConnectionName = TRUE;
                }
                // 必須：指定DBに対応していない
                if (!isset($row['type']) || !in_array($row['type'], $this->_validDbTypes)) {
                    $row['type'] = (!isset($row['type']))?'NULL':$row['type'];
                    return $this->_setError('is not database Type.['.$row['type'].']');
                }
                // 必須：ホスト指定がない
                if (!isset($row['hostspec']) || !is_string($row['hostspec']) || strlen($row['hostspec'])<=0) {
                    $row['hostspec'] = (!isset($row['hostspec']))?'NULL':$row['hostspec'];
                    return $this->_setError('is not database host.['.$row['hostspec'].']');
                }
                // 必須：DB名指定がない
                if (!isset($row['database']) || !is_string($row['database']) || strlen($row['database'])<=0) {
                    $row['database'] = (!isset($row['database']))?'NULL':$row['database'];
                    return $this->_setError('is not database name.['.$row['database'].']');
                }
                // 必須：ユーザ名指定がない
                if (!isset($row['username']) || !is_string($row['username']) || strlen($row['username'])<=0) {
                    $row['username'] = (!isset($row['username']))?'NULL':$row['username'];
                    return $this->_setError('is not database user name.['.$row['username'].']');
                }
                // 必須：パスワード指定がない
                if (!isset($row['password']) || !is_string($row['password']) || strlen($row['password'])<=0) {
                    $row['password'] = (!isset($row['password']))?'NULL':$row['password'];
                    return $this->_setError('is not database password.['.$row['password'].']');
                }
                // ポート指定がある場合、数値ではない場合
                if (isset($row['port']) && (!is_numeric($row['port']) && $row['port']!='')) {
                    $row['port'] = (!isset($row['port']))?'NULL':$row['port'];
                    return $this->_setError('is not database port.['.$row['port'].']');
                }
                // ソケット指定がある場合、文字列ではない場合
                if (isset($row['socket']) && (!is_string($row['socket']))) {
                    $row['socket'] = (!isset($row['socket']))?'NULL':$row['socket'];
                    return $this->_setError('is not database socket.['.$row['socket'].']');
                }
            }
            
            // 標準選択DB設定名定義が定義されていない
            if (!$defaultDbConnectionName) {
                return $this->_setError('is not default database connect setting.');
            }
        }
        return TRUE;
    }
    
    
    
    /**
     * DB用オブジェクト生成
     * 
     * DB用オブジェクト生成処理。
     * 
     * @since 1.0
     * @access protected
     * @param string  $type DBタイプ名
     * @return object
     */
    function _databaseObjectInstantiation($type = '')
    {
        $file = sprintf('%s/%s/FTDB_%s.class.php', PATH_DATA_LIB, $type, ucfirst($type));
        
        // ファイルの存在有無確認
        if (!is_file($file)) {
            return FALSE;
        }
        
        // 実体化
        require_once $file;
        $objName = 'FTDB_' . ucfirst($type);
        $obj = call_user_func(array($objName, 'factory'));
        
        return $obj;
    }
    
    
    
    /**
     * データ登録処理
     * 
     * データを登録します。
     * 
     * @access public
     * @return string
     */
    function insert()
    {
        
    }
    
    
    
    /**
     * データ更新処理
     * 
     * データを更新します。
     * 
     * @access public
     * @return string
     */
    function update()
    {
        
    }
    
    
    
    /**
     * データ削除処理
     * 
     * データを削除します。
     * 
     * @access public
     * @return string
     */
    function delete()
    {
        
    }
    
    
    
    /**
     * 全データ削除処理
     * 
     * 全てのデータを削除します。
     * 
     * @access public
     * @return string
     */
    function deleteAll()
    {
        
    }
    
    
    
    /**
     * データ1件取得処理
     * 
     * データを1件取得します。
     * 
     * @access public
     * @return string
     */
    function getRow()
    {
        
    }
    
    
    
    /**
     * データ取得処理
     * 
     * データを取得します。
     * 
     * @access public
     * @return string
     */
    function select($where = array(), $getColumn = array(), $db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->select($where, $getColumn, $db, $this->_tableName);
    }
    
    
    
    /**
     * データ取得処理
     * 
     * データを取得します。
     * 
     * @access public
     * @return string
     */
    function count($where = array(), $db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->count($where, $db, $this->_tableName);
    }
    
    
    
    /**
     * DB接続処理
     * 
     * DB接続処理を行います。
     * 
     * @access public
     * @return string
     */
    function query($sql = '', $db = DEFAULT_DB_CONNECTION_NAME)
    {
        if (!is_string($sql) || strlen($sql)<=0) {
            return FALSE;
        }
        
        return $this->_dbObj->query($sql, $db);
    }
    
    
    
    /**
     * DB接続確認処理
     * 
     * DB接続確認を行います。
     * 
     * @access public
     * @return string
     */
    function ping($db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->ping($this->_databaseSetting[$db], $db);
    }
    
    
    
    /**
     * 実行SQL文取得処理
     * 
     * 実行したSQL文を取得する処理
     * 
     * @access private
     * @return boolean
     */
    function traceSql()
    {
        if (!isset($GLOBALS['forty-two']['FTDB']['sql'])) {
            return FALSE;
        }
        return $GLOBALS['forty-two']['FTDB']['sql'];
    }
    
    
    
    /**
     * DB接続処理
     * 
     * DB接続処理を行います。
     * 
     * @access private
     * @return boolean
     */
    function _setnames($chara_set = CHARA_SET_DATABASE, $db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->_setnames($chara_set, $db);
    }
    
    
    
    /**
     * テーブル構造情報の取得
     * 
     * 指定DB、テーブル構造情報の取得
     * 
     * @access public
     * @return string
     */
    function _getTableInfo($table = NULL, $db = DEFAULT_DB_CONNECTION_NAME, $cache = TRUE)
    {
        $table = is_null($table)?$this->_tableName:$table;
        
        $name = sprintf('%s_%s_%s_%s', PREFIX_DB_CACHE_FILE, 'tableInfo', $db, $table);
        
        // キャッシュから取得可能 and キャッシュ利用する
        $tableInfo = CC::cacheGet($name, PATH_DB_INFO);
        if (is_array($tableInfo) && count($tableInfo)>0 && $cache) {
            return $tableInfo;
        } else {
            // キャッシュがない場合DBから取ってくる
            $tableInfo = $this->_dbObj->_getTableInfo($table, $db, $cache);
            
            // キャッシュ作成
            CC::cacheSet($tableInfo, $name, PATH_DB_INFO);
            return $tableInfo;
        }
        
        return FALSE;
    }
    
    
    
    /**
     * DB接続処理
     * 
     * DB接続処理を行います。
     * 
     * @access private
     * @return boolean
     */
    function _connect($db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->_connect($this->_databaseSetting[$db], $db);
    }
    
    
    
    /**
     * DB接続切断処理
     * 
     * DB接続を切断する処理を行います。
     * 
     * @access private
     * @return string
     */
    function _close($db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->_dbObj->_close($this->_databaseSetting[$db], $db);
    }
    
    
    
    /**
     * エラー取得
     * 
     * エラーを取得します。
     * 
     * @access private
     * @return string
     */
    function getError()
    {
        if (isset($this->_errorMsg)
        && is_array($this->_errorMsg)
        && count($this->_errorMsg)>0) {
            return $this->_errorMsg;
        }
        return FALSE;
    }
    
    
    
    /**
     * エラー詳細取得
     * 
     * エラー詳細を取得します。
     * 
     * @access private
     * @return string
     */
    function _getErrorDetail()
    {
        if (isset($this->_errorMsg)
        && is_array($this->_errorMsg)
        && count($this->_errorMsg)>0) {
            $error = array();
            foreach ($this->_errorMsg as $k => $row) {
                $error[$k]['error'] = $row;
                if (isset($this->_errorMsgDetail[$k])) {
                    $error[$k]['errorDetail'] = $this->_errorMsgDetail[$k];
                }
            }
            return $error;
        }
        return FALSE;
    }
    
    
    
    /**
     * エラー設定
     * 
     * エラーを設定します。
     * 
     * @access private
     * @return FALSE
     */
    function _setError($errorMessage = '')
    {
        $dbtAll = debug_backtrace();
        $dbt = $dbtAll[0];
        
        $dbt['line'] = isset($dbt['line'])?$dbt['line']:$dbtAll[1]['line'];
        $dbt['file'] = isset($dbt['file'])?$dbt['file']:$dbtAll[1]['file'];
        $dbt['file'] = basename($dbt['file']);
        
        $this->_errorMsgDetail[] = $dbtAll;
        $this->_errorMsg[] = 
            sprintf('%s(%s) %s',$dbt['file'], $dbt['line'], $errorMessage);
        
        return FALSE;
    }
    
    
    
    /**
     * エラー詳細メッセージ取得
     * 
     * エラーナンバーからエラー詳細を取得。
     * 
     * @access private
     * @param  string or number  $errorNo エラーナンバー
     * @return string
     */
    function _getDetailedErrorMessage($errorNo = NULL)
    {
        return FALSE;
    }
    
    /**
     * コンストラクタ
     * 
     * @access private
     * @param  mixed
     * @return mixed
     */
    function __construct($tableName = '', $db = DEFAULT_DB_CONNECTION_NAME)
    {
        return $this->FTDB_Common($tableName, $db);
    }
}



/**
 * DB ショートコール クラス
 */
class FTDB extends FTDB_Common
{
}
?>